#ifndef CHEETAH_GRAPHICS_TEXTURE_GL_H
#define CHEETAH_GRAPHICS_TEXTURE_GL_H

#include "cheetah/core.h"
#include <GL/glew.h>

#include "cheetah/graphics/image.h"
#include "cheetah/graphics/cube_image.h"

namespace cheetah {
namespace graphics {
class TextureGL {
public:
  TextureGL(uint32 target) : target_(target) { glGenTextures(1, &tex_id_); }
  virtual ~TextureGL() { 
    if (tex_id_ > 0) {
      glDeleteTextures(1, &tex_id_);
      tex_id_ = 0;
    }
  }

  void Enable() const { glEnable(this->target()); }
  void Disable() const { glDisable(this->target()); }
  void Bind() const { glBindTexture(this->target(), this->tex_id()); }
  void Unbind() const { glBindTexture(this->target(), 0); }

  void SetDefaultParameters() const {
    this->Bind();
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    this->SetParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    this->SetParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    this->SetParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    this->SetParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  }

  void SetParameter(uint32 name, int32 val) const {
    glTexParameteri(target_, name, val);
  }
  void SetParameter(uint32 name, int32 *val) const {
    glTexParameteriv(target_, name, val);
  }
  void SetParameter(uint32 name, float val) const {
    glTexParameterf(target_, name, val);
  }
  void SetParameter(uint32 name, float *val) const {
    glTexParameterfv(target_, name, val);
  }

  uint32 tex_id() const { return tex_id_; }
  uint32 target() const { return target_; }
  bool is_valid() const { return 0 != this->tex_id(); }
private:
  uint32 tex_id_;
  uint32 target_;

  DISABLE_COPY_AND_ASSIGN(TextureGL);
};

class CubeTexture : public TextureGL {
public:
  CubeTexture() : TextureGL(GL_TEXTURE_CUBE_MAP) { }
  bool Initialize(int w, int h, int i_format) const;
  bool Initialize(const CubeImage &image, int i_format) const;
  bool Initialize(const Image *images[6], int i_format) const;
};

class Texture2D : public TextureGL {
public:
  Texture2D() : TextureGL(GL_TEXTURE_2D) { }
  Texture2D(uint32 target) : TextureGL(target) { }
  bool Initialize(int w, int h, int i_format) const;
  bool Initialize(const Image &image, int i_format) const;
  bool InitializeDepth(int w, int h, int i_format) const;

  void GenerateMipmap() const { 
    this->Bind();
    glGenerateMipmap(this->target()); 
  }
  int width() const {
    int w;
    this->Bind();
    glGetTexLevelParameteriv(this->target(), 0, GL_TEXTURE_WIDTH, &w);
    return w;
  }
  int height() const {
    int h;
    this->Bind();
    glGetTexLevelParameteriv(this->target(), 0, GL_TEXTURE_HEIGHT, &h);
    return h;
  }
  void Save(const char *filename, int level = 0) const;
};

class Texture2DArray : public TextureGL {
public:
  Texture2DArray() : TextureGL(GL_TEXTURE_2D_ARRAY) { }
  bool Initialize(int w, int h, int z, int i_format) const;
  void Save(const char *filename, int z, int level = 0) const;
};
} // graphics
} // cheetah

#endif // CHEETAH_GRAPHICS_TEXTURE_GL_H
